package x.ovo.wechat.bot;

import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.db.nosql.redis.RedisDS;
import lombok.extern.slf4j.Slf4j;
import redis.clients.jedis.Jedis;
import x.ovo.wechat.bot.common.entity.event.MessageEvent;
import x.ovo.wechat.bot.common.entity.message.Message;
import x.ovo.wechat.bot.common.entity.message.TextMessage;
import x.ovo.wechat.bot.common.enums.MessageType;
import x.ovo.wechat.bot.common.event.Event;
import x.ovo.wechat.bot.common.event.EventListener;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 统计信息侦听器
 * 监听群消息，统计每日发言情况
 *
 * @author ovo on 2024/05/09.
 * @since 1.0.0
 */
@Slf4j
public class StatisticsListener extends EventListener<MessageEvent<Message>> {

    private final RedisDS ds = RedisDS.create();

    private static final String TOTAL_COUNT_TEMPLATE = "statistics:total:count::{}";
    private static final String EVERDAY_COUNT_TEMPLATE = "statistics:everday:count::{}";
    private static final String EVERONE_COUNT_TEMPLATE = "statistics:everone:count::{}";
    private static final String TODAY_COUNT_TEMPLATE = "statistics:today:count::{}:{}";
    private static final String ANALYZE_TEMPLATE = "statistics:analyze::{}";
    private static final List<String> KFC_ARGS = List.of("kfc", "肯德基", "v我50", "疯狂星期四");

    public StatisticsListener() {
        log.info("redis连接状态：{}", this.ds.getJedis().ping());
    }

    @Override
    public boolean support(Event<?> event) {
        return event instanceof MessageEvent
                && event.getSource() != null
                && ((Message) event.getSource()).isGroup()
                && Objects.nonNull(((Message) event.getSource()).getMember())
                && !MessageType.RECALLED.equals(((Message) event.getSource()).getType());
    }

    @Override
    public void onEvent(MessageEvent<Message> event) {
        Message message = event.getSource();
        log.debug("[群消息统计] 接收到新消息");
        try {
            this.statistics(message);
            this.analyze(message);
            this.show(message);
        } catch (Exception e) {
            log.error("[群消息统计] 统计失败", e);
        }
    }

    /**
     * 向群内显示排行
     *
     * @param message 消息
     */
    private void show(Message message) {
        // 获取发送消息的群成员昵称
        String callUserNickName = message.getMember().getNickName();
        // 判断当前消息是否为文本消息，发送消息的群成员是否具有权限
        if (!(message instanceof TextMessage && this.plugin.getUsers().contains(callUserNickName))) return;

        String[] args = message.getContent().split(" ");
        if (Objects.equals(args[0], "今日排行")) {
            this.showToday(message);
        } else if (Objects.equals(args[0], "昨日排行")) {
            this.showYesterday(message);
        } else if (Objects.equals(args[0], "本周排行")) {
            this.showThisWeek(message);
        } else if (Objects.equals(args[0], "本月排行")) {
            this.showThisMonth(message);
        } else if (Objects.equals(args[0], "历史排行")) {
            this.showHistory(message);
        }
    }

    private String getNameAndStatistics(Map.Entry<String, ?> entry, String groupNickName, String memberNickName) {
        return Optional.ofNullable(this.context.getContactManager().getGroupMemberByNickName(groupNickName, memberNickName))
                .map(member -> StrUtil.format("{}: {}", member.getDisplayName().isBlank() ? member.getNickName() : StrUtil.format("{}({})", member.getNickName(), member.getDisplayName()), entry.getValue()))
                .orElse(null);
    }

    private void showToday(Message message) {
        String today = DateUtil.today();
        String groupName = message.getFromUser().getNickName();
        String groupUserName = message.getFromUser().getUserName();
        String[] args = message.getContent().split(" ");
        String key = StrUtil.format(TODAY_COUNT_TEMPLATE, groupName, today);
        int top = args.length > 1 ? Integer.parseInt(args[1]) : 10;
        // 获取redis中今日数据，并按照value进行排序，取top
        Jedis jedis = this.ds.getJedis();
        Map<String, String> map = jedis.hgetAll(key);
        List<String> list = map.entrySet().stream()
                .filter(entry -> !entry.getKey().startsWith(ANALYZE_TEMPLATE.replace("{}", "")))
                .sorted(Comparator.<Map.Entry<String, String>>comparingInt(entry -> Integer.parseInt(entry.getValue())).reversed())
                .limit(top)
                .map(entry -> this.getNameAndStatistics(entry, groupName, entry.getKey()))
                .filter(Objects::nonNull)
                .toList();
        String analyzeResult = this.getAnalyzeResult(map);
        String s = StrUtil.format("今日排行前 {} :\n{}\n---------------\n{}", top, StrUtil.join("\n", list), analyzeResult);
        this.context.getApi().sendText(message.getFromUser().getUserName(), s);
        jedis.close();
    }

    private void showYesterday(Message message) {
        String groupName = message.getFromUser().getNickName();
        String groupUserName = message.getFromUser().getUserName();
        String[] args = message.getContent().split(" ");
        Jedis jedis = this.ds.getJedis();
        int top = args.length > 1 ? Integer.parseInt(args[1]) : 10;
        // 获取昨日日期
        String yesterday = DateUtil.format(DateUtil.yesterday(), "yyyy-MM-dd");
        Map<String, String> map = jedis.hgetAll(StrUtil.format(TODAY_COUNT_TEMPLATE, groupName, yesterday));
        List<String> list = map.entrySet().stream()
                .filter(entry -> !entry.getKey().startsWith(ANALYZE_TEMPLATE.replace("{}", "")))
                .sorted(Comparator.<Map.Entry<String, String>>comparingInt(entry -> Integer.parseInt(entry.getValue())).reversed())
                .limit(top)
                .map(entry -> this.getNameAndStatistics(entry, groupName, entry.getKey()))
                .filter(Objects::nonNull)
                .toList();
        String analyzeResult = this.getAnalyzeResult(map);
        String s = StrUtil.format("昨日排行前 {} :\n{}\n---------------\n{}", top, StrUtil.join("\n", list), analyzeResult);
        this.context.getApi().sendText(message.getFromUser().getUserName(), s);
    }

    private void showThisWeek(Message message) {
        String groupName = message.getFromUser().getNickName();
        String groupUserName = message.getFromUser().getUserName();
        String[] args = message.getContent().split(" ");
        Jedis jedis = this.ds.getJedis();
        int top = args.length > 1 ? Integer.parseInt(args[1]) : 10;
        // 获取本周日期
        List<DateTime> list = DateUtil.rangeToList(DateUtil.beginOfWeek(DateUtil.date()), DateUtil.endOfWeek(DateUtil.date()), DateField.DAY_OF_WEEK);
        // 遍历本周日期组装key
        Map<String, Integer> map = list.stream()
                .map(s -> DateUtil.format(s, "yyyy-MM-dd"))
                .map(day -> StrUtil.format(TODAY_COUNT_TEMPLATE, groupName, day))
                .map(jedis::hgetAll)
                .flatMap(m -> m.entrySet().stream())
                .collect(Collectors.groupingBy(Map.Entry::getKey, Collectors.summingInt(entry -> Integer.parseInt(entry.getValue()))));
        List<String> result = map.entrySet().stream()
                .filter(entry -> !entry.getKey().startsWith(ANALYZE_TEMPLATE.replace("{}", "")))
                .sorted(Comparator.<Map.Entry<String, Integer>>comparingInt(Map.Entry::getValue).reversed())
                .limit(top)
                .map(entry -> this.getNameAndStatistics(entry, groupName, entry.getKey()))
                .filter(Objects::nonNull)
                .toList();
        String analyzeResult = this.getAnalyzeResult(map);
        String s = StrUtil.format("本周排行前 {} :\n{}\n---------------\n{}", top, StrUtil.join("\n", result), analyzeResult);
        this.context.getApi().sendText(message.getFromUser().getUserName(), s);
        jedis.close();
    }

    private void showThisMonth(Message message) {
        String groupName = message.getFromUser().getNickName();
        String groupUserName = message.getFromUser().getUserName();
        String[] args = message.getContent().split(" ");
        Jedis jedis = this.ds.getJedis();
        int top = args.length > 1 ? Integer.parseInt(args[1]) : 10;
        // 获取本月日期
        List<DateTime> list = DateUtil.rangeToList(DateUtil.beginOfMonth(DateUtil.date()), DateUtil.endOfMonth(DateUtil.date()), DateField.DAY_OF_MONTH);
        Map<String, Integer> collect = list.stream()
                .map(s -> DateUtil.format(s, "yyyy-MM-dd"))
                .map(day -> StrUtil.format(TODAY_COUNT_TEMPLATE, groupName, day))
                .map(jedis::hgetAll)
                .flatMap(map -> map.entrySet().stream())
                .collect(Collectors.groupingBy(Map.Entry::getKey, Collectors.summingInt(entry -> Integer.parseInt(entry.getValue()))));
        List<String> result = collect
                .entrySet().stream()
                .filter(entry -> !entry.getKey().startsWith(ANALYZE_TEMPLATE.replace("{}", "")))
                .sorted(Comparator.<Map.Entry<String, Integer>>comparingInt(Map.Entry::getValue).reversed())
                .limit(top)
                .map(entry -> this.getNameAndStatistics(entry, groupName, entry.getKey()))
                .filter(Objects::nonNull)
                .toList();
        String analyzeResult = this.getAnalyzeResult(collect);
        String s = StrUtil.format("本月排行前 {} :\n{}\n---------------\n{}", top, StrUtil.join("\n", result), analyzeResult);
        this.context.getApi().sendText(message.getFromUser().getUserName(), s);
    }

    private void showHistory(Message message) {
        String groupName = message.getFromUser().getNickName();
        String groupUserName = message.getFromUser().getUserName();
        String[] args = message.getContent().split(" ");
        int top = args.length > 1 ? Integer.parseInt(args[1]) : 10;
        Jedis jedis = this.ds.getJedis();
        String groupTotalKey = StrUtil.format(TOTAL_COUNT_TEMPLATE, groupName);
        Long total = jedis.get(groupTotalKey) == null ? 0 : Long.parseLong(jedis.get(groupTotalKey));
        String userKey = StrUtil.format(EVERONE_COUNT_TEMPLATE, groupName);
        Map<String, String> map = jedis.hgetAll(userKey);
        List<String> list = map.entrySet().stream()
                .filter(entry -> !entry.getKey().startsWith(ANALYZE_TEMPLATE.replace("{}", "")))
                .sorted(Comparator.<Map.Entry<String, String>>comparingInt(entry -> Integer.parseInt(entry.getValue())).reversed())
                .limit(top)
                .map(entry -> this.getNameAndStatistics(entry, groupName, entry.getKey()))
                .filter(Objects::nonNull)
                .toList();
        String analyzeResult = this.getAnalyzeResult(map);
        String s = StrUtil.format("群消息历史前 {} ({}条):\n{}\n---------------\n{}", top, total, StrUtil.join("\n", list), analyzeResult);
        this.context.getApi().sendText(message.getFromUser().getUserName(), s);
    }

    private void statistics(Message message) {
        String groupName = message.getFromUser().getNickName();
        String nickName = message.getMember().getNickName();

        String today = DateUtil.today();
        // 群消息总数+1
        String total_count = StrUtil.format(TOTAL_COUNT_TEMPLATE, groupName);
        Jedis jedis = this.ds.getJedis();
        jedis.incr(total_count);
        log.debug("群消息总数+1");
        // 群每日消息总数+1
        String everday_count = StrUtil.format(EVERDAY_COUNT_TEMPLATE, groupName);
        jedis.hincrBy(everday_count, today, 1);
        log.debug("群每日消息总数+1");
        // 群成员消息总数+1
        String everone_count = StrUtil.format(EVERONE_COUNT_TEMPLATE, groupName);
        jedis.hincrBy(everone_count, nickName, 1);
        log.debug("群成员消息总数+1");
        // 群成员今日消息总数+1
        String today_count = StrUtil.format(TODAY_COUNT_TEMPLATE, groupName, today);
        jedis.hincrBy(today_count, nickName, 1);
        jedis.expire(today, TimeUnit.DAYS.toSeconds(15));
        log.debug("群成员今日消息总数+1");
        jedis.disconnect();
        jedis.close();
    }

    private void analyze(Message message) {
        String groupName = message.getFromUser().getNickName();
        Jedis jedis = this.ds.getJedis();
        String today = DateUtil.today();

        String key = StrUtil.format(ANALYZE_TEMPLATE, message.getType());
        // 统计今日的消息类型
        jedis.hincrBy(StrUtil.format(TODAY_COUNT_TEMPLATE, groupName, today), key, 1);
        jedis.hincrBy(StrUtil.format(EVERONE_COUNT_TEMPLATE, groupName), key, 1);

        // 分析是否是疯四文案并记录
        if (message instanceof TextMessage) {
            String content = message.getContent();
            KFC_ARGS.forEach(arg -> {
                if (content.contains(arg)) {
                    jedis.hincrBy(StrUtil.format(TODAY_COUNT_TEMPLATE, groupName, today), StrUtil.format(ANALYZE_TEMPLATE, "KFC"), 1);
                    jedis.hincrBy(StrUtil.format(EVERONE_COUNT_TEMPLATE, groupName), StrUtil.format(ANALYZE_TEMPLATE, "KFC"), 1);
                }
            });
        }
        jedis.close();
    }

    private String getAnalyzeResult(Map<String, ?> map) {
        String prefix = ANALYZE_TEMPLATE.replace("{}", "");
        String string = map.entrySet().stream()
                .filter(entry -> entry.getKey().startsWith(prefix) && !entry.getKey().contains("KFC"))
                .map(entry -> StrUtil.format("{} {} 条", MessageType.valueOf(entry.getKey().replace(prefix, "")).getName(), entry.getValue()))
                .collect(Collectors.joining("，"));
        Object count = map.get(StrUtil.format(ANALYZE_TEMPLATE, "KFC"));
        return string + (Objects.isNull(count) ? "" : StrUtil.format("，疯狂星期四文案 {} 条", count));
    }
}
